package network.tcp.nio;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @program: bio
 * @description:
 * @author: zhanghz001
 * @create: 2020-10-27 13:46
 **/
public class NioServer {
    public static void main(String[] args) {
        try {
            //1. 打开一个服务端通道
            ServerSocketChannel serverChannel = ServerSocketChannel.open();

            // 绑定对应的端口号
            SocketAddress local = new InetSocketAddress(10000);
            serverChannel.bind(local);

            //通道默认是阻塞的，需要设置为非阻塞
            serverChannel.configureBlocking(false);
            //4.打开一个选择器
            //Selector ‐‐‐ 选择器
            // SelectionKey ‐‐‐ 绑定通道后返回那个令牌
            // SelectableChannel ‐‐‐ 可以使用选择器的通道
            Selector selector = Selector.open();

            //绑定选择器和服务器通道
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);

            //4此时没有门卫大爷，所以需要经常看一下有没有连接发过来没？
            while (true) {
                System.out.println("11");

                //选择器会监控客户端的状态
                int count = selector.select();

                System.out.println("222");
                if (count > 0) {
                    System.out.println("有客户端来连接了");

                    //7.会遍历所有的服务端通道.看谁准备好了,谁准备好了,就让谁去连接.
                    // 获取所有服务端通道的令牌,并将它们都放到一个集合中,将集合返回
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        //每一个服务器通道的令牌
                        SelectionKey selectionKey = iterator.next();
                        //
                        if (selectionKey.isAcceptable()) {
                            //可以通过令牌,获取到一个已经就行的服务器通道
                            ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
                            //客户端的延伸通道

                            SocketChannel socketChannel = ssc.accept();
                            //将客户端的延伸通道设置为非阻塞的
                            socketChannel.configureBlocking(false);
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            //当客户端来链接的时候,步骤已经全部执行完毕

                        } else if (selectionKey.isReadable()) {
                            //当通道已经做好了读取的准备
                            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                            // 6. 获取客户端传递过来的数据,并把数据放在byteBuffer1这个缓冲区中
                            ByteBuffer bytes = ByteBuffer.allocate(1024);
                            int len;
                            while ((len = socketChannel.read(bytes)) > 0) {
                                bytes.flip();
                                System.out.println(new String(bytes.array(), 0, len));
                                bytes.clear();
                            }

                            // 7. 给客户端回写数据
                            socketChannel.write(ByteBuffer.wrap("服务器回话了".getBytes()));
                            socketChannel.shutdownOutput();
                            socketChannel.close();
                        }
                        iterator.remove();

                    }
                }

            }

            // 8. 释放资源
            // serverChannel.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
    }
}
